package com.afdor.rws.dlock.zookeeper;

import com.afdor.rws.annotation.extension.Extension;
import com.afdor.rws.dlock.lock.ReentrantLock;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.zookeeper.KeeperException;

import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * zookeeper实现的分布式可重入锁
 *
 * @author 悭梵
 * @date Created in 2018-08-31 11:36
 */
@Slf4j
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@Extension(value = "zookeeper", scope = Extension.Scope.PROTOTYPE, order = Extension.NORMAL_ORDER + 100, interfaceClass = ReentrantLock.class)
public class DistributedReentrantLock implements ReentrantLock {

    private static final String ROOT_PATH = "/RWS/Distributed/ReentrantLock/";

    /**
     * 线程池
     */
    private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(2);
    /**
     * 锁的ID,对应zk一个PERSISTENT节点,下挂EPHEMERAL节点.
     */
    @Getter
    protected String path;
    /**
     * zk的客户端
     */
    @Getter
    protected CuratorFramework client;
    /**
     * zk 共享锁实现
     */
    @Getter
    protected InterProcessMutex interProcessMutex = null;

    public DistributedReentrantLock(CuratorFramework client, String lockId) {
        init(client, lockId);
    }

    public void init(CuratorFramework client, String lockId) {
        if (StringUtils.isBlank(lockId)) {
            throw new NullPointerException("锁标识为空");
        }
        if (client == null) {
            throw new NullPointerException("Curator客户端为空");
        }
        lockId = StringUtils.trim(lockId);
        this.client = client;
        this.path = ROOT_PATH + lockId;
        this.interProcessMutex = new InterProcessMutex(client, this.path);
    }


    @Override
    public void lock() {
        try {
            tryLock(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
        try {
            return interProcessMutex.acquire(timeout, unit);
        } catch (InterruptedException e) {
            throw e;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public void unlock() {
        try {
            interProcessMutex.release();
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
        } finally {
            executorService.schedule(new Cleaner(client, path), 0, TimeUnit.MILLISECONDS);
        }
    }


}

@Slf4j
class Cleaner implements Runnable {
    final String path;
    final CuratorFramework client;

    public Cleaner(CuratorFramework client, String path) {
        this.path = path;
        this.client = client;
    }

    @Override
    public void run() {
        try {
            List list = client.getChildren().forPath(path);
            if (list == null || list.isEmpty()) {
                client.delete().forPath(path);
            }
        } catch (KeeperException.NoNodeException | KeeperException.NotEmptyException e) {
            //nothing
        } catch (Exception e) {
            //准备删除时,正好有线程创建锁
            log.error(e.getMessage(), e);
        }
    }
}
